import { createActions, handleActions } from 'redux-actions';
import _ from 'lodash';
import { message } from 'antd';
import { getCustomAttributeList } from '@api/customAttribute';
import { getAllOrganization } from '@api/organization';
import { getStaffList } from '@api/staff';
import { getAllIndustry } from '@api/asset';
import { getAllHasPocOnedayList, getAllStrategies, getDetectionCache } from '@api/task';
import {
  getAssetStats,
  loadAssets,
  getAssetCategoryList,
  exportAssetForm,
  exportAssetReport
} from '@api/asset';
import { ORIGIN_DESC_MAP } from '@util/constants';
import moment from 'moment';
import filterQuery from '@util/filter-query';

export const DataRangeFilterType = 'data-range-filter-type';
export const InputFilterType = 'input-filter-type';
export const TreeSelectFilterType = 'tree-select-filter-type';

const search = decodeURIComponent(
  location.search || sessionStorage.getItem('assetManage.search') || ''
).replace(/^\?/, '');

// 提取筛选条件
function getAssetsQuery(attributeFilter, search) {
  // 路径前缀正则
  const posPrefixRe = /^(\d+_)+\$_/;
  const query = _.pickBy(
    _.mapValues(
      _.omitBy(attributeFilter, (v, key) => {
        return _.includes(['moreAction', 'location'], key) || /^CUSTOM_ATTRIBUTE_/.test(key);
      }),
      ({ type, value }, key) => {
        if (type === InputFilterType) {
          return [value];
        } else if (type === TreeSelectFilterType) {
          return _.map(value, 'value').map(v => {
            let r = v;
            if (typeof r === 'string') {
              r = r.replace(posPrefixRe, '');
            }
            if (key === 'port') {
              r = Number(r);
            }
            return r;
          });
        } else if (type === DataRangeFilterType && !_.isEmpty(value)) {
          return [
            moment(value[0])
              .startOf('day')
              .format('YYYY-MM-DD HH:mm:ss'),
            moment(value[1])
              .endOf('day')
              .format('YYYY-MM-DD HH:mm:ss')
          ];
        }
      }
    ),
    v => !_.isEmpty(v)
  );

  if (search) {
    query.search = search;
  }

  // 提取自定义属性筛选
  const customAttribute = [];
  for (let key in attributeFilter) {
    if (/^CUSTOM_ATTRIBUTE_/.test(key)) {
      const attributeOid = key.replace(/^CUSTOM_ATTRIBUTE_/, '');
      const { value, type } = attributeFilter[key] || {};
      if (type === InputFilterType && value) {
        customAttribute.push({
          attributeOid: attributeOid,
          text: value
        });
      } else if (!_.isEmpty(value)) {
        customAttribute.push({
          attributeOid: attributeOid,
          value: _.map(value, 'value').filter(Boolean)
        });
      }
    }
  }
  if (customAttribute.length) {
    query.customAttribute = customAttribute;
  }

  // 转化地理位置
  if (!_.isEmpty(_.get(attributeFilter, 'location.value'))) {
    query.location = _.filter(_.map(attributeFilter.location.value, 'location'), Boolean);
  }

  return query;
}

const initState = {
  // 是否存在资产
  existSite: false,

  // 资产统计
  assetStats: {
    total: [],
    assetType: [],
    customAttribute: [],
    assetCategory: [],
    organization: [],
    location: [],
    waf: [],
    service: [],
    os: [],
    oneday: [],
    tag: [],
    area: [],
    origin: []
  },

  //#region 搜索相关
  // 属性筛选，包含moreAction项，请求时需要过滤该值.
  // 每一项结构为: {
  //    type: DataRangeFilterType | InputFilterType | TreeSelectFilterType,
  //    value: []
  // }
  // 对于 TreeSelectFilterType 组件的value字段, 结构是{label:"label",value:"value"}，
  // 因为要附加其他信息，比如location，所以TreeSelect组件需要设置labelInValue属性,使用时需要_.map(data,"value")提取值
  attributeFilter: {
    /**
     * @examples
     * moreAction: { type: TreeSelectFilterType, value: [{ label: '组件',value: 'component' }] },
     * component: { type: TreeSelectFilterType, value: [{ label: 'nginx', value: 'nginx' }] }
     */
  },
  search: filterQuery(search), // 模糊搜索
  pagaition: {
    // 分页
    page: 1,
    pageSize: 10
  },
  sort: {
    by: 'created', // 排序项
    direction: -1 // 排序方向，-1为倒序，1为正序
  },
  //#endregion

  loadingAsset: true, // 加载资产列表中

  customAttributeList: [], // 自定义属性列表
  organizationTree: [], // 组织架构树
  assetCategoryList: [], // 资产分类树
  assetCategoryTree: [], // 资产分类树
  areaList: [], // 资产区域列表
  allIndustries: [], //行业列表
  staffList: [], // 人员列表
  templates: [], // 策略模板列表
  onedays: [], // 漏洞情报列表

  currentSelectedSites: [], // 当前选中的资产
  selectedAllSite: false, // 是否全选
  currentEditSite: {}, // 当前编辑的资产
  currentAssetList: [], // 当前资产列表
  currentAssetCount: 0, // 当前资产数
  currentAssetId: '', // 当前资产详情展示的资产的ID
  currentAssetUpdated: '', // 当前资产详情展示的资产的更新时间，与currentAssetId组合，有变化时刷新资产详情
  loadAssetListIndex: 0, // 用于强制更新资产列表，更新时设置为Date.now()
  loadAssetIndex: 0, // 用于在资产详情内触发更新，更新时设置为Date.now()

  assetSettingModalVisible: false, // 资产设置弹窗visible状态
  batchImportModalVisible: false, // 批量导入弹窗visible状态
  detectionTaskModalVisible: '', // 资产探测弹窗或Web指纹探测弹窗visible状态, 取值: Zoomeye / Web
  onedayTaskModalVisible: false, // 1DAY探测弹窗visible状态
  exportModalType: '', // 导出报告报表类型，类型为空时，不显示弹窗
  zoomeyeDetectionConfig: {}, // 资产探测配置
  webDetectionConfig: {}, // web指纹探测配置
  honeypotDetectionConfig: {} // 蜜罐探测配置
};

let loadAssetStatsCallback = null;

export const actions = createActions({
  initAssetManage: () => dispatch => {
    //初始化当前选中资产、筛选、搜索

    dispatch(actions.loadCustomAttributeList());
    dispatch(actions.loadOrganizationTree());
    dispatch(actions.loadStaffList());
    dispatch(actions.loadAssetCategoryList());
    dispatch(actions.loadAssetStats());
    dispatch(actions.loadAssets());
    dispatch(actions.loadAssetIndustry());
    dispatch(actions.loadDetectionCache());
    dispatch(actions.loadAllStrategies());
    dispatch(actions.loadAllHasPocOnedayList());
  },
  initStaffList: () => dispatch => {
    dispatch(actions.loadStaffList());
    dispatch(actions.loadOrganizationTree());
  },
  clearCurrentSelectAndSearch: () => dispatch => {
    //清除当前选中资产、筛选、搜索
    dispatch(actions.setState({ currentSelectedSites: [] }));
    dispatch(actions.setState({ attributeFilter: [] }));
    dispatch(actions.setState({ search: search }));
  },
  requestLoadAssetStatsCallback: () => (dispatch, getState) => {
    if (loadAssetStatsCallback) {
      return;
    }
    // 绑定一个缓存更新的监听事件
    const socketIO = _.get(getState(), 'socketIO.socketIO');
    if (socketIO) {
      loadAssetStatsCallback = () => {
        dispatch(actions.cancelLoadAssetStatsCallback());
        dispatch(actions.loadAssetStats());
      };
      socketIO.on('cache.updated', loadAssetStatsCallback, { cacheKey: 'site-stats' });
    } else {
      dispatch(actions.loadAssetStats());
    }
  },
  cancelLoadAssetStatsCallback: () => (dispatch, getState) => {
    // 取消所有缓存更新的监听事件
    const socketIO = _.get(getState(), 'socketIO.socketIO');
    if (socketIO && loadAssetStatsCallback) {
      socketIO.cancel('cache.updated', loadAssetStatsCallback);
    }
    loadAssetStatsCallback = null;
  },
  loadAssetStats: () => dispatch => {
    getAssetStats().then(res => {
      const assetStats = _.get(res, ['data', 'data', 'stats']);

      //#region 处理默认区域
      _.forEach(_.get(assetStats, 'area', []), item => {
        if (item.name === null) {
          item.name = '默认区域';
        }
      });
      //#endregion

      //#region 处理地理位置
      _.forEach(_.get(assetStats, 'location', []), item => {
        if (item.name === '局域网') {
          item.children = [];
        }
      });
      //#endregion

      //#region 处理来源
      const originMap = {};
      _.forEach(_.get(assetStats, 'origin', []), item => {
        const key = ORIGIN_DESC_MAP[item.name] || item.name;
        if (!originMap[key]) {
          originMap[key] = {
            _id: item.name,
            name: key,
            count: item.count
          };
        } else {
          originMap[key].count += item.count;
        }
      });
      assetStats.origin = _.values(originMap);

      //#endregion

      //#region 处理操作系统分类
      const transformOsNullAsOther = (oss, parentName) => {
        return _.map(oss, item => {
          if (item.name === null) {
            item.name = parentName;
          }
          item.children = transformOsNullAsOther(_.get(item, 'children', []), item.name);
          return item;
        });
      };
      assetStats.os = transformOsNullAsOther(_.get(assetStats, 'os', []), '其他');
      //#endregion

      //#region 将路径信息填充到树数据中（针对左侧过滤筛选树列表）
      const fillTreeDataKeyWithPos = (data, pos = '0') => {
        _.forEach(data, (item, index) => {
          if (_.isObject(item)) {
            const currentPos = `${pos}_${index}`;
            item.realId = item._id;
            item._id = `${currentPos}_$_${item._id || item.name}`;

            if (!_.isEmpty(item.children)) {
              fillTreeDataKeyWithPos(item.children, currentPos);
            }
          }
        });
      };
      // 不需要添加路径信息的key
      const notTreeDataKeys = [
        'component',
        'count',
        'port',
        'riskComponent',
        'riskPort',
        'riskService',
        'customAttribute'
      ];
      for (let key in assetStats) {
        if (notTreeDataKeys.includes(key)) {
          continue;
        }
        fillTreeDataKeyWithPos(assetStats[key]);
      }
      //#endregion

      const update = { assetStats };

      dispatch(actions.setState(update));
    });
  },
  loadCustomAttributeList: () => dispatch => {
    const params = {
      search: '',
      page: 1,
      pageSize: 200
    };
    getCustomAttributeList(params)
      .then(res => {
        const customAttributeList = _.get(res, ['data', 'data', 'list'], []).filter(
          attr => attr.status
        );
        dispatch(actions.setState({ customAttributeList }));
      })
      .catch(() => {
        message.error('获取资产自定义属性失败');
      });
  },
  loadAssetIndustry: () => dispatch => {
    getAllIndustry()
      .then(res => {
        let allIndustries = _.get(res, 'data.data.list', []);
        dispatch(actions.setState({ allIndustries }));
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadStaffList: () => dispatch => {
    getStaffList({ isAll: true })
      .then(res => {
        let staffList = _.get(res, 'data.data.list', []);
        dispatch(actions.setState({ staffList }));
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadOrganizationTree: () => dispatch => {
    getAllOrganization()
      .then(res => {
        let organizationTree = _.get(res, 'data.data.tree', []);
        dispatch(actions.setState({ organizationTree }));
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadAssetCategoryList: () => dispatch => {
    getAssetCategoryList()
      .then(res => {
        const assetCategoryList = _.get(res, 'data.data.list', []);
        // 将第一层顶往前提
        const sortAssetCategoryList = [];
        sortAssetCategoryList.push(
          ...assetCategoryList.filter(r => !r.parent),
          ...assetCategoryList.filter(r => r.parent)
        );
        const categoryMap = {};
        for (const category of sortAssetCategoryList) {
          if (!category.parent) {
            category.children = [];
            categoryMap[category._id] = category;
          } else if (categoryMap[category.parent]) {
            categoryMap[category.parent].children.push(category);
          }
        }
        dispatch(
          actions.setState({
            assetCategoryList,
            assetCategoryTree: Object.values(categoryMap)
          })
        );
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadAllStrategies: () => dispatch => {
    getAllStrategies()
      .then(res => {
        const templates = _.get(res, 'data.data.list', []);
        dispatch(actions.setState({ templates }));
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadAllHasPocOnedayList: () => dispatch => {
    getAllHasPocOnedayList()
      .then(res => {
        let onedays = _.get(res, 'data.data.list', []);
        dispatch(actions.setState({ onedays }));
      })
      .catch(err => {
        window.console.log(err);
      });
  },
  loadAssets: payload => (dispatch, getState) => {
    const enableLoading = _.get(payload, 'enableLoading', true);
    const syncCurrentAssetUpdated = _.get(payload, 'syncCurrentAssetUpdated', true);
    const state = getState();
    const {
      search,
      attributeFilter = {},
      pagaition = {},
      sort = {},
      currentAssetId,
      currentAssetUpdated
    } = _.get(state, 'assetManage', {});
    let newSearch = search;
    if (search.indexOf('publicquery') > -1 && search.indexOf('origin') > -1) {
      newSearch = search.replace('publicquery', 'zoomeye.org');
    }
    const query = getAssetsQuery(attributeFilter, newSearch);
    if (enableLoading) {
      dispatch(actions.setState({ loadingAsset: true }));
    }
    loadAssets({
      query,
      pagaition,
      sort
    })
      .then(res => {
        const { existSite, list = [], count = 0 } = _.get(res, 'data.data', {});
        let nextCurrentAssetId = currentAssetId;
        let nextCurrentAssetUpdated = currentAssetUpdated;

        // 更新资产详情的资产ID和更新时间
        const currentAsset = currentAssetId && _.find(list, i => i._id === currentAssetId);
        if (!currentAsset) {
          nextCurrentAssetId = _.get(list, '0._id', '');
          nextCurrentAssetUpdated = _.get(list, '0.updated', '');
        } else if (currentAsset && syncCurrentAssetUpdated) {
          nextCurrentAssetUpdated = _.get(currentAsset, 'updated', '');
        }

        dispatch(
          actions.setState({
            existSite,
            currentAssetList: list,
            currentAssetCount: count,
            currentAssetId: nextCurrentAssetId,
            currentAssetUpdated: nextCurrentAssetUpdated
          })
        );
      })
      .catch(() => {
        message.error('获取资产列表失败');
      })
      .finally(() => {
        dispatch(actions.setState({ loadingAsset: false }));
      });
  },
  exportAssetForm: () => (dispatch, getState) => {
    const state = getState();
    const { search, attributeFilter = {}, sort = {}, currentSelectedSites } = _.get(
      state,
      'assetManage',
      {}
    );

    //获取勾选资产
    const siteIds = _.map(currentSelectedSites, '_id');
    const query = siteIds.length ? { siteIds } : getAssetsQuery(attributeFilter, search);
    return exportAssetForm({ query, sort });
  },
  exportAssetReport: () => (dispatch, getState) => {
    const state = getState();
    const { search, attributeFilter = {}, sort = {}, currentSelectedSites } = _.get(
      state,
      'assetManage',
      {}
    );

    const siteIds = _.map(currentSelectedSites, '_id');
    const query = siteIds.length ? { siteIds } : getAssetsQuery(attributeFilter, search);
    return exportAssetReport({
      query,
      sort
    });
  },
  loadDetectionCache: () => dispatch => {
    getDetectionCache().then(res => {
      const result = _.get(res, 'data.data.result', {});
      const cacheObj = {};
      if (!_.isEmpty(result)) {
        for (let type in result) {
          cacheObj[`${type.toLowerCase()}DetectionConfig`] = _.omit(result[type], [
            'created',
            'updated',
            'scanType',
            'userOid'
          ]);
          _.set(
            cacheObj[`${type.toLowerCase()}DetectionConfig`],
            'interfaces',
            _.get(result[type], 'interface')
          );
          const intervalTime = _.get(result[type], 'intervalTime');
          const intervalUnit = _.get(result[type], 'intervalUnit');
          _.set(
            cacheObj[`${type.toLowerCase()}DetectionConfig`],
            'intervalTime',
            intervalTime < 1 ? 1 : intervalTime
          );
          _.set(
            cacheObj[`${type.toLowerCase()}DetectionConfig`],
            'intervalUnit',
            !intervalUnit ? 'hour' : intervalUnit
          );
        }
      }
      dispatch(actions.setState({ ...cacheObj }));
    });
  },
  setState: value => value,
  resetQuery: payload => dispatch => {
    // 重置筛选项，用于从其他页面跳转到资产列表
    const state = {
      attributeFilter: {},
      search: '',
      pagaition: { page: 1, pageSize: 10 },
      sort: { by: 'created', direction: -1 },
      ...payload
    };

    dispatch(actions.setState(state));
  }
});

const reducer = handleActions(
  {
    setState: (state, action) => {
      const { error, payload } = action;
      if (error || typeof payload !== 'object') {
        window.console.error(error);
        return state;
      } else if (typeof payload === 'object') {
        return { ...state, ...payload };
      } else {
        window.console.error('payload must be an object in setState');
        return state;
      }
    }
  },
  initState
);

export default reducer;
